﻿namespace NTLib.Core.UTest.Threading
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Core.Threading;

    [TestClass]
    public class SemaphoreSlimExtTest
    {

        #region Methods

        [TestCategory("Core.Threading")]
        [TestMethod]
        public void SemaphoreSlimLock_LockTest()
        {
            SemaphoreSlim locker = new SemaphoreSlim(1);
            CancellationTokenSource source = new CancellationTokenSource();
            source.CancelAfter(5000);
            using (var lockerItem = locker.ScopeWait(source.Token))
            {
                Assert.AreEqual(locker.CurrentCount, 0);
            }

            Assert.AreEqual(locker.CurrentCount, 1);
        }

        [TestCategory("Core.Threading")]
        [TestMethod]
        public void SemaphoreSlimLock_LockTestAsync()
        {
            SemaphoreSlim locker = new SemaphoreSlim(1);
            var task = locker.ScopeWaitAsync();
            task.Wait();
            using (var lockerItem = task.Result)
            {
                Assert.AreEqual(locker.CurrentCount, 0);

                CancellationTokenSource source = new CancellationTokenSource();
                source.CancelAfter(2000);
                try
                {
                    locker.ScopeWaitAsync(source.Token).Wait();
                    Assert.Fail("The wait is not good.");
                }
                catch (AggregateException ex)
                {
                    Assert.IsTrue(ex.InnerException is TaskCanceledException);
                }
            }

            Assert.AreEqual(locker.CurrentCount, 1);
            CancellationTokenSource source2 = new CancellationTokenSource();

            source2.CancelAfter(5000);
            task = locker.ScopeWaitAsync(source2.Token);
            task.Wait();
            using (var lockerItem = task.Result)
            {
                Assert.AreEqual(locker.CurrentCount, 0);
            }
        }

        [TestCategory("Core.Threading")]
        [TestMethod]
        public void SemaphoreSlimLock_ReadTestAsync_withCancel()
        {
            SemaphoreSlim locker = new SemaphoreSlim(1);
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            CancellationTokenSource tokenSourceSafty = new CancellationTokenSource();

            tokenSourceSafty.CancelAfter(10000);
            using (var lockerItem2 = locker.ScopeWait(tokenSourceSafty.Token))
            {
                try
                {
                    tokenSource.CancelAfter(1000);
                    TaskCompletionSource<bool> t = new TaskCompletionSource<bool>();
                    ThreadPool.QueueUserWorkItem((e) =>
                       {
                           try
                           {
                               using (var lockerItem = locker.ScopeWait(tokenSource.Token))
                               {
                                   Assert.Fail("Shouln't reach this code section");
                               }
                           }
                           catch (OperationCanceledException)
                           {

                           }
                           catch (Exception ex)
                           {
                               t.SetException(ex);
                               return;
                           }
                           t.SetResult(true);
                       });
                    t.Task.Wait();

                }
                catch (OperationCanceledException)
                {

                }
                catch
                {
                    Assert.Fail("Shouln't reach this code section");
                }
            }
        }

        #endregion
    }
}
